/*
 * Copyright 2022-2023 Bytedance Ltd. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.bytedance.bitsail.base.metrics.manager.BitSailMetricManager;
import com.bytedance.bitsail.common.configuration.BitSailConfiguration;
import com.bytedance.bitsail.common.option.CommonOptions;
import com.bytedance.bitsail.component.metrics.prometheus.AbstractPrometheusReporter;
import com.bytedance.bitsail.component.metrics.prometheus.impl.PrometheusPushGatewayReporter;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.SlidingWindowReservoir;
import com.codahale.metrics.Timer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

public class MetricReporterTest {
  private final Gauge<Integer> mockedGauge = Mockito.spy(new TestGauge());
  private final Counter mockedCounter = Mockito.spy(new Counter());
  private final Histogram mockedHistogram = Mockito.spy(new Histogram(new SlidingWindowReservoir(10)));
  private final Meter mockedMeter = Mockito.spy(new Meter());
  private final Timer mockedTimer = Mockito.spy(new Timer());

  private AbstractPrometheusReporter reporter;
  private BitSailMetricManager metricManager;

  /**
   * because it can't pass windows UT test, so comment it
   * private PushGatewayContainer pushGatewayContainer;
   * private static final DockerImageName PUSH_GATEWAY_DOCKER_IMAGE = DockerImageName.parse("prom/pushgateway:latest");
   */
  @Before
  public void init() throws Exception {
    /**
     * because it can't pass windows UT test, so comment it
     * pushGatewayContainer = new PushGatewayContainer(PUSH_GATEWAY_DOCKER_IMAGE);
     * pushGatewayContainer.start();
     * pushGatewayContainer.waitingFor(Wait.defaultWaitStrategy());
     */

    BitSailConfiguration jobConf = BitSailConfiguration.newDefault();
    jobConf.set(CommonOptions.JOB_ID, -1L);
    jobConf.set(CommonOptions.METRICS_REPORTER_TYPE, "nop");
    metricManager = new BitSailMetricManager(jobConf, "group");
    reporter = new PrometheusPushGatewayReporter();
    jobConf.set(CommonOptions.INSTANCE_ID, 666L);
    jobConf.set(CommonOptions.JOB_NAME, "metric_report_test");
    reporter.open(jobConf);
    reporter.notifyOfAddedMetric(mockedGauge, "gauge", metricManager);
    reporter.notifyOfAddedMetric(mockedCounter, "counter", metricManager);
    reporter.notifyOfAddedMetric(mockedHistogram, "histogram", metricManager);
    reporter.notifyOfAddedMetric(mockedMeter, "meter", metricManager);
    reporter.notifyOfAddedMetric(mockedTimer, "timer", metricManager);
  }

  /**
   * @After because it can't pass windows UT test, so comment it.
   * public void close() throws Exception {
   *   pushGatewayContainer.close();
   * }
   */

  // @Test because it can't pass windows UT test, so comment it.
  public void testNotifyOfAddedMetric() {
    reporter.report();
    Mockito.verify(mockedGauge, Mockito.times(1)).getValue();
    Mockito.verify(mockedCounter, Mockito.times(1)).getCount();
    Mockito.verify(mockedHistogram, Mockito.times(1)).getSnapshot();
    Mockito.verify(mockedMeter, Mockito.times(1)).getCount();
    Mockito.verify(mockedTimer, Mockito.times(1)).getSnapshot();
  }

  // @Test because it can't pass windows UT test, so comment it.
  public void testNotifyOfRemovedMetric() {
    reporter.notifyOfRemovedMetric(mockedGauge, "gauge", metricManager);
    reporter.notifyOfRemovedMetric(mockedHistogram, "histogram", metricManager);
    reporter.report();
    Mockito.verify(mockedGauge, Mockito.times(0)).getValue();
    Mockito.verify(mockedCounter, Mockito.times(1)).getCount();
    Mockito.verify(mockedHistogram, Mockito.times(0)).getSnapshot();
    Mockito.verify(mockedMeter, Mockito.times(1)).getCount();
    Mockito.verify(mockedTimer, Mockito.times(1)).getSnapshot();
  }

  @Test
  public void testValidateMetricName() {
    String validMetricName = "metric_name_test";
    Assert.assertEquals(validMetricName, reporter.validateMetricName(validMetricName));

    String invalidMetricName = "metric-name-test";
    Assert.assertEquals(validMetricName, reporter.validateMetricName(invalidMetricName));
  }

  static class TestGauge implements Gauge<Integer> {
    @Override
    public Integer getValue() {
      return 1;
    }
  }
}
